using System;
using System.Collections.Generic;
using System.Text;
using System.IO;
using System.Runtime.Serialization.Formatters.Binary;
using System.Web.Security;
using System.Security.Cryptography;
using System.Web.UI;

namespace Impact.Infrastructure
{
    /// <summary>
    /// global utility functions
    /// </summary>
    public static class Utility
    {

        /// <summary>
        /// remove any non-alphanumeric caharacters (excluding hyphen '-' and underscore '_')
        /// from the given string
        /// </summary>
        public static string StripNonAlphaNumeric( string s )
        {
            string pattern = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ1234567890-_";
            return StripNotMatching( s, pattern );
        }

        /// <summary>
        /// removes any characters from the string s that are not in the pattern
        /// </summary>
        public static string StripNotMatching( string s, string pattern )
        {
            string output = string.Empty;
            if ( s == null ) throw new ArgumentException( "input string cannot be null" );

            foreach ( char c in s )
            {
                if ( pattern.Contains( c.ToString() ) )
                {
                    output += c;
                }
            }
            return output;
        }


        /// <summary>
        /// generate a random string of the given length, using only alpha characters
        /// </summary>
        public static string RandomString( int length )
        {
            StringBuilder output = new StringBuilder();
            Random random = new Random();
            for ( int i = 0; i < length; i++ )
            {
                int num = random.Next( 52 );
                if ( num > 25 ) num += 71;
                else num += 65;
                output.Append( char.ConvertFromUtf32( num ) );
            }
            return output.ToString();
        }

        /// <summary>
        /// convert an object to a blob
        /// </summary>
        public static byte[] Serialize( object value )
        {
            MemoryStream ms = new MemoryStream();
            BinaryFormatter bin = new BinaryFormatter();
            bin.Serialize( ms, value );
            return ms.ToArray();
        }


        /// <summary>
        /// converts a raw object (from db BLOB) to it's typed form
        /// </summary>
        public static object Deserialize( object result )
        {
            object output = null;
            try
            {
                // deserialize binary to object
                MemoryStream ms = new MemoryStream( (byte[])result );
                ms.Position = 0;
                BinaryFormatter bin = new BinaryFormatter();
                output = bin.Deserialize( ms );
            }
            catch
            {
                output = null;
            }
            return output;
        }

        /// <summary>
        /// Retrieve the human-readable username for the user Guid given
        /// </summary>
        public static string GetUsername( Guid userId )
        {

            string username = string.Empty;
            if ( usernameCache == null ) usernameCache = new Dictionary<Guid, string>();
            if ( usernameCache.ContainsKey( userId ) )
            {
                username = usernameCache[userId];
            }
            else
            {
                MembershipUser user = System.Web.Security.Membership.GetUser( userId );
                if ( user != null )
                    username = user.UserName;
                else
                    username = "Unknown";
                usernameCache.Add( userId, username );
            }

            return username;
        }

        private static Dictionary<Guid, string> usernameCache;



        /// <summary>
        /// Produces a string containing an encrypted string for the given object
        /// suitable for use in an HTTP cookie given a CustomIdentity
        /// </summary>
        /// <returns>Encrypted string</returns>
        public static string Encrypt( object target )
        {
            string encryptedString = String.Empty;

            byte[] inputBytes = Utility.Serialize( target );
            byte[] pwdhash = null;
            MD5CryptoServiceProvider hashmd5;

            //generate an MD5 hash from the password. 
            //a hash is a one way encryption meaning once you generate
            //the hash, you cant derive the password back from it.
            hashmd5 = new MD5CryptoServiceProvider();
            pwdhash = hashmd5.ComputeHash( ASCIIEncoding.ASCII.GetBytes( encryptionKey ) );
            hashmd5 = null;

            // Create a new TripleDES service provider 
            TripleDESCryptoServiceProvider tdesProvider = new TripleDESCryptoServiceProvider();
            tdesProvider.Key = pwdhash;
            tdesProvider.Mode = CipherMode.ECB;

            return Convert.ToBase64String(
                tdesProvider.CreateEncryptor().TransformFinalBlock( inputBytes, 0, inputBytes.Length ) );

        }


        /// <summary>
        /// Returns an instance of a CustomIdentity class, 
        /// given an encrypted authentication string obtained from an HTTP cookie.
        /// </summary>
        /// <param name="encryptedInput">Encrypted string conataining User Identity</param>
        /// <returns>CustomIdentity object</returns>
        public static object Decrypt( string encryptedInput )
        {
            byte[] inputBytes = Convert.FromBase64String( encryptedInput );
            byte[] pwdhash = null;
            MD5CryptoServiceProvider hashmd5;

            //generate an MD5 hash from the password. 
            //a hash is a one way encryption meaning once you generate
            //the hash, you cant derive the password back from it.
            hashmd5 = new MD5CryptoServiceProvider();
            pwdhash = hashmd5.ComputeHash( ASCIIEncoding.ASCII.GetBytes( encryptionKey ) );
            hashmd5 = null;

            // Create a new TripleDES service provider 
            TripleDESCryptoServiceProvider tdesProvider = new TripleDESCryptoServiceProvider();
            tdesProvider.Key = pwdhash;
            tdesProvider.Mode = CipherMode.ECB;

            byte[] decrypted = tdesProvider.CreateDecryptor().TransformFinalBlock( inputBytes, 0, inputBytes.Length );

            return Utility.Deserialize( decrypted );

        }

        private static string encryptionKey = "Ma9eDj093nAdk03i";


        /// <summary>
        /// converts the given string into a bool
        /// default to false.
        /// </summary>
        public static bool StringAsBool( string input )
        {
            bool output = false;
            Boolean.TryParse( input, out output );
            return output;
        }

        /// <summary>
        /// find the control with the given ID, recursively below the root
        /// </summary>
        public static Control FindControlRecursive( ControlCollection root, string id )
        {
            foreach ( Control control in root )
            {
                if ( control != null && id.Equals( control.ID, StringComparison.InvariantCultureIgnoreCase ) )
                {
                    return control;
                }
                else
                {
                    Control result = FindControlRecursive( control.Controls, id );
                    if ( result != null )
                    {
                        return result;
                    }
                }
            }

            return null;
        }

        /// <summary>
        /// Finds all controls with the given type anywhere under the root
        /// </summary>
        public static List<Control> FindControlsRecursive( ControlCollection root, Type toFind )
        {
            List<Control> controls = new List<Control>();
            foreach ( Control control in root )
            {
                if ( control != null && ( control.GetType() == toFind || control.GetType().IsSubclassOf( toFind ) ) )
                {
                    controls.Add( control );
                }
                else
                {
                    controls.AddRange( FindControlsRecursive( control.Controls, toFind ) );
                }
            }
            return controls;
        }

    }
}
